package com.android.BluethootChess.control;

import java.util.HashMap;
import java.util.Map;

import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.ImageView;

import com.android.BluethootChess.connectivity.IComunication;
import com.android.BluethootChess.model.GameBoardMatrix;
import com.android.BluethootChess.model.PieceColor;
import com.android.BluethootChess.model.PieceTypeAndColor;
import com.android.BluethootChess.model.Square;
import com.android.BluethootChess.model.SquarePosition;
import com.android.BluethootChess.rules.KingRules;
import com.android.BluethootChess.rules.KnightRules;
import com.android.BluethootChess.rules.NewBishopRules;
import com.android.BluethootChess.rules.NewQueenRules;
import com.android.BluethootChess.rules.NewRookRules;
import com.android.BluethootChess.rules.PawnRules;
import com.android.BluethootChess.rules.PieceRule;
import com.android.BluethootChess.ui.Drawing;
import com.android.BluethootChess.ui.UISquares;
import com.android.BluethootChess.util.Utils;

public class TouchController implements OnTouchListener {

	private Bitmap bitmap;
	private final Point point;
	private View view;
	private Drawable imageDrawable;
	private UISquares squares;
	private GameBoardMatrix gameMatrix;
	private SquarePosition oldSelectedPos;
	private Drawing draw;
	private int nrClick = 0;
	private SquarePosition crtClickPos;
	private Point[] points;
	private int[] boardColors;
	private boolean needInit = true;
	private Map<Integer, PieceRule> mapRules;
	private PieceRule[] rules = new PieceRule[PieceTypeAndColor.values().length];
	private int rulesNumber = 0;

	public TouchController(UISquares squares, int[] boardColors,
			GameBoardMatrix game) {
		point = new Point();
		this.squares = squares;
		this.gameMatrix = game;
		points = new Point[2];
		this.boardColors = boardColors;
		initPieceRules();
	}

	void firstMoveByWhite() {
		Thread t = new Thread() {
			public void run() {
				while (true) {
					waitMove();
					Utils.logFile("Points from server: " + points[0] + points[1]);
					makeFullMove(points[0], points[1]);
					Utils.logFile("FMBW: " + points[0] + " , " + points[1]);
					try {
						Utils.logFile("FMBW: Sleep");
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		};
		t.start();
	}

	private void initPieceRules() {
		mapRules = new HashMap<Integer, PieceRule>();
		loadRules();
		int pieceCode = 0;
		for (PieceRule rule : rules) {
			mapRules.put(pieceCode, rule);
			mapRules.put(pieceCode + rulesNumber / 2, rule);
			pieceCode++;
		}
	}

	private void loadRules() {
		rules[rulesNumber++] = new NewRookRules(gameMatrix, PieceColor.BLACK);
		rules[rulesNumber++] = new KnightRules(gameMatrix, PieceColor.BLACK);
		rules[rulesNumber++] = new NewBishopRules(gameMatrix, PieceColor.BLACK);
		rules[rulesNumber++] = new NewQueenRules(gameMatrix, PieceColor.BLACK);
		rules[rulesNumber++] = new KingRules(gameMatrix, PieceColor.BLACK);
		rules[rulesNumber++] = new PawnRules(gameMatrix, PieceColor.BLACK);

		rules[rulesNumber++] = new NewRookRules(gameMatrix, PieceColor.WHITE);
		rules[rulesNumber++] = new KnightRules(gameMatrix, PieceColor.WHITE);
		rules[rulesNumber++] = new NewBishopRules(gameMatrix, PieceColor.WHITE);
		rules[rulesNumber++] = new NewQueenRules(gameMatrix, PieceColor.WHITE);
		rules[rulesNumber++] = new KingRules(gameMatrix, PieceColor.WHITE);
		rules[rulesNumber++] = new PawnRules(gameMatrix, PieceColor.WHITE);
	}

	public boolean onTouch(View v, MotionEvent event) {
		view = v;
		if (needInit == true) {
			initDrawable(view);
			firstMoveByWhite();
			nrClick=-1;
		}

		int x = (int) event.getX();
		int y = (int) event.getY();
		int highlightColor = Utils.HIGHLIGHT_COLOR;

		if (x <= bitmap.getWidth() && y <= bitmap.getHeight()) {

			point.x = x;
			point.y = y;
			nrClick++;
			crtClickPos = squares.findPointSquare(point);

			if (crtClickPos != null) {
				int actualColor = draw.getSquareColorAt(crtClickPos.x, crtClickPos.y);
				draw.highlightSelectedSquare(crtClickPos, highlightColor, actualColor);
			}

			if (oldSelectedPos == null) { // la prima mutare
				oldSelectedPos = new SquarePosition(crtClickPos.x, crtClickPos.y);
			} else {
				draw.cleanSelectedSquare(oldSelectedPos, highlightColor);
				makeFullMove(oldSelectedPos, crtClickPos);
				updateView();

				if (nrClick == 2) {
					points[0] = oldSelectedPos;
					points[1] = crtClickPos;
					sendMoveCoords(points);

					oldSelectedPos = null;
					nrClick = 0;
				}

				/*
				 * waitMove(); // asteapta mutarea de la celalat player
				 * makeFullMove(points[0], points[1]); // face mutarea
				 */}
			updateView();
		}
		return false;
	}

	private void makeFullMove(Point prevPoint, Point clickPos) {
		int mapRuleKey = gameMatrix.getGameBoard()[prevPoint.x][prevPoint.y];

		Utils.logFile("makeFullMove int: " + mapRuleKey);
		
		if (mapRuleKey != -1) {
			PieceRule rule = mapRules.get(mapRuleKey);
			if (rule != null) {
				rule.setPiecePosition(prevPoint);
				if (rule.canMove(clickPos.x, clickPos.y)) {
					Utils.logFile("INTRI AICI????");
					makeUIMove(prevPoint, clickPos);
					movePiece(prevPoint, clickPos);
				}
			}
		}
	}

	/**
	 * Trimite o lista de puncte la server.
	 * 
	 * @param points
	 *            - lista de puncte ce se vor trimite.
	 * */
	void sendMoveCoords(Point[] points) {
		String pointsAsString = "";
		for (Point p : points) {
			pointsAsString += p.x + ":" + p.y + ";";
		}

		Utils.logFile("SMoveCoords Se trimite: " + pointsAsString);

		Utils.COMUNICATION.sendString(pointsAsString);
	}

	private void makeUIMove(Point prevPoint, Point clickPos) {
		Square fromSquare = squares.getSquares()[prevPoint.x][prevPoint.y];
		Square toSquare = squares.getSquares()[clickPos.x][clickPos.y];
		Point from = fromSquare.getLowerPoint();
		Point dest = toSquare.getLowerPoint();

		if (from.x != dest.x || from.y != dest.y) {
			draw.movePiece(from, dest, draw.getSquareColorAt(prevPoint.x,
					prevPoint.y), draw.getSquareColorAt(dest.x, dest.y));
		}
	}

	/**
	 * Asteapta pana cand al doilea jucator face o mutare. Se modifica array-ul
	 * points.
	 * */
	void waitMove() {
		IComunication comunication = Utils.COMUNICATION;
		String string = comunication.recvString();
		Utils.logFile("WAIT MOVE: " + string);
		String[] split = string.split(";");
		int i = 0;
		for (String s : split) {
			String[] coords = s.split(":");
			int x = Integer.parseInt(coords[0]);
			int y = Integer.parseInt(coords[1]);
			points[i++] = new Point(x, y);
		}
	}

	// TODO sterge metoda asta, e doar pt test.
	void printMatrix() {
		byte matrix[][] = gameMatrix.getGameBoard();
		String s = "";
		for (int i = 0; i < matrix.length; i++) {
			s = "";
			for (int j = 0; j < matrix[i].length; j++) {
				s += matrix[i][j] + " ";
			}
			// System.out.println(s);
			Utils.logFile(s);
		}
	}

	/**
	 * Muta o piesa pe tabla reprezentata ca matrice, nu pe UI.
	 * */
	private void movePiece(Point from, Point dest) {
		gameMatrix.getGameBoard()[dest.x][dest.y] = gameMatrix.getGameBoard()[from.x][from.y];
		gameMatrix.getGameBoard()[from.x][from.y] = -1;
	}

	public void setBitmap(Bitmap bitmap) {
		this.bitmap = bitmap;
	}

	private void initDrawable(View view) {
		if (imageDrawable == null) {
			imageDrawable = new BitmapDrawable(bitmap);
			((ImageView) view).setImageDrawable(imageDrawable);
		}
		if (draw == null) {
			draw = new Drawing(bitmap, view.getContext());
			draw.setBoardColors(boardColors);
			draw.setSquares(squares);
		}
		needInit = false;
	}

	/**
	 * Face update la view. Trebuie apelata ori de cate ori se modifica o
	 * imagine.
	 * */
	public void updateView() {
		imageDrawable.invalidateSelf();
	}

}
